#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <getopt.h>

typedef void *(THREAD_BODY) (void *thread_arg);		//定义指向函数的指针，函数接受void* 型的参数,返回void* 类型的结果

void *thread_worker(void *ctx);		//线程要执行的函数
int thread_start(pthread_t * thread_id, THREAD_BODY * thread_worbody, void *thread_arg);	//线程属性初始化

//命令行参数提示：如果参数输入错误，将提示
void print_usage(char *progname)
{
		printf("%s usage: \n", progname);
		printf("-p(--port): sepcify server listen port.\n");
		printf("-h(--Help): print this help information.\n");

		return ;
}


int main(int argc, char **argv)
{
		int					sockfd = -1;
		int					rv = -1;
		struct sockaddr_in	servaddr;
		struct sockaddr_in	cliaddr;
		socklen_t			len;
		int					port = 0;		//端口
		int					clifd;
		int					ch;
		int					on = 1;
		pthread_t			tid;

		//命令行参数解析
		struct option		opts[] = {
				{"port", required_argument, NULL, 'p'},			//定义长选项 --port，需要一个参数
				{"help", no_argument, NULL, 'h'},				//定义长选项 --help，不需要参数
				{NULL, 0, NULL, 0}		//结束标记，表述数组结束
		};

		while( (ch=getopt_long(argc, argv, "p:h", opts, NULL)) != -1 )
		{
				switch(ch)
				{
						case 'p':
							port=atoi(optarg);
							break;

						case 'h':
							print_usage(argv[0]);
							return 0;
				}
		}

		if( !port )
		{
				print_usage(argv[0]);
				return 0;
		}

		sockfd=socket(AF_INET, SOCK_STREAM, 0);
		if(sockfd < 0)
		{
				printf("Create socket failure: %s\n", strerror(errno));
				return -1;
		}
		printf("Create socket[%d] successfully!\n", sockfd);

		setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));		//避免端口重复使用的报错,一定要在bind前调用，bind之后调用不生效

		memset(&servaddr, 0, sizeof(servaddr));
		servaddr.sin_family=AF_INET;
		servaddr.sin_port = htons(port);		//(网络字节序，大端字节序)主机字节顺序转换成网络字节顺序(把一个16位数高低位互换)
		servaddr.sin_addr.s_addr = htonl(INADDR_ANY);		//监听所有IP地址
		//inet_aton("192.168.0.16", &servaddr.sin_addr);监听特定IP

		rv=bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
		if(rv < 0)
		{
				printf("Socket[%d] bind on port[%d] failure: %s\n", sockfd, port, strerror(errno));
				return -2;
		}

		listen(sockfd, 13);		//13 是指定操作系统应该为到来的连接请求排队的最大数目。被称为“backlog”,等待队列的最大长度
		printf("Star to listen on port [%d]\n", port);

		while(1)
		{
				printf("Start accept new client incoming...\n");

				clifd=accept(sockfd, (struct sockaddr *)&cliaddr, &len);	//&len指针，表示（客户端地址结构体）大小
				if(clifd<0)
				{
						printf("Accept new client failure: %s\n", strerror(errno));
						continue;
				}

				printf("Accpet new client[%s:%d] successfully!\n", inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));

				thread_start(&tid, thread_worker, (void *)clifd);
		}

		close(sockfd);

		return 0;
}

int thread_start(pthread_t * thread_id, THREAD_BODY * thread_workbody, void *thread_arg)
{
		int					rv = -1;
		pthread_attr_t		thread_attr;

		if( pthread_attr_init(&thread_attr) )	//非零报错->初始化
		{
				printf("pthread_attr_init() failure: %s\n", strerror(errno));
				goto CleanUp;
		}

		if( pthread_attr_setstacksize(&thread_attr, 120*1024) )		//设置栈大小
		{
				printf("pthread_attr_setstacksize() failure: %s\n", strerror(errno));
				goto CleanUp;
		}
		
		if( pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) )	//设置为分离状态
		{
				printf("pthread_attr_setdetachstate() failure: %s\n", strerror(errno));
				goto CleanUp;
		}

		if( pthread_create(thread_id, &thread_attr, thread_workbody, thread_arg) )
		{
				printf("Create thread failure: %s\n", strerror(errno));
				goto CleanUp;
		}

		rv = 0;

CleanUp:
		pthread_attr_destroy(&thread_attr);
		return rv;
}

void *thread_worker(void *ctx)
{
		int					clifd;
		int					rv;
		char				buf[1024];
		int					i;

		if( !ctx )	//ctx为clifd，若clifd为0则报错
		{
				printf("Invalid input argument in %s()\n", __FUNCTION__);
				pthread_exit(NULL);
		}

		clifd = (int)ctx;	//前面把clifd强制转换成void型，要将其恢复为int

		printf("Child thread start to communicate with socket client...\n");

		while(1)
		{
				memset(buf, 0, sizeof(buf));
				rv=read(clifd, buf, sizeof(buf));
				if(rv < 0)
				{
						printf("Read data from client sockfd[%d] failure: %S", strerror(errno));
						close(clifd);
						pthread_exit(NULL);
				}
				else if(rv == 0)
				{
						printf("Socket[%d] get disconnected an thread will exit.\n", clifd);
						close(clifd);
						pthread_exit(NULL);
				}
				else if(rv > 0)
				{
						printf("Read %d bytes data from server: %s\n", rv, buf);
				}

				rv=write(clifd, buf, rv);
				if(rv < 0)
				{
						printf("Write to client by socket[%d] failure: %S and thread will exit\n", clifd, strerror(errno));
						close(clifd);
						pthread_exit(NULL);
				}
		}
}
